package com.thinkcoer.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect
@Slf4j
@Component
@Order(2)
public class LogAspect {



//    @Pointcut(value = "execution(* com..service..*(..)) "+
//            "&&@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
//            "||@annotation(org.springframework.web.bind.annotation.GetMapping)" +
//            "||@annotation(org.springframework.web.bind.annotation.PostMapping)" +
//            "||@annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
//            "||@annotation(org.springframework.web.bind.annotation.PatchMapping)")
    @Pointcut(value = "execution(* com..controller..*(..)) ")
    public void logPointCut(){ }

    /***
     *方法前执行
     * @param joinPoint
     * @return
     */
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        log.info("【日志切面:Before方法执行了】");
        StringBuilder str = this.getMethodInfo(joinPoint);
        if (CollectionUtils.arrayToList(joinPoint.getArgs()).isEmpty()) {
            str.append("该方法无参数");
        } else {
            StringBuilder strArgs = new StringBuilder("【请求参数】:");
            for (Object o : joinPoint.getArgs()) {
                strArgs.append(o + ",");
            }
            str.append(strArgs);
        }
        log.info(str.toString());
    }


    /***
     * 于Before增强处理和AfterReturing增强,
     * Around增强处理可以决定目标方法在什么时候执行，如何执行，甚至可以完全阻止目标方法的执行
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        log.info("【日志切面:执行目标方法前Around方法执行】");
        StringBuilder sb = this.getMethodInfo(point);

        long startTime = System.currentTimeMillis();
        //执行方法
        Object returnVal = point.proceed();
        //计算耗时
        long elapsedTime = System.currentTimeMillis() - startTime;
        log.info("【日志切面:执行目标方法后Around方法执行】");
        sb.append("【请求消耗时长" + elapsedTime + "ms】");

        log.info(sb.toString());
        return returnVal;
    }

    //注意在AfterThrowing及After注解中不能有JoinPoint参数
    @After(value = "logPointCut()")
    public void doAfter(){
        log.info("【日志切面:After方法执行了】");
    }

    /***
     * 方法执行完后执行
     * @param point
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint point,Object ret) {
        log.info("【日志切面:AfterReturning方法执行了】");
        StringBuilder sb = this.getMethodInfo(point);
        if(ObjectUtils.isEmpty(ret)){
            sb.append("【请求返回结果没有返回值】");
        }else{
            sb.append("【请求返回结果】:"+ret.toString());
        }

        log.info(sb.toString());
    }

    /***
     * 请求方法信息
     * @param point
     */
    private StringBuilder getMethodInfo(JoinPoint point){
        StringBuilder sb = new StringBuilder();
        sb.append("【方法名】"+point.getSignature().getDeclaringTypeName()+"."+point.getSignature().getName());
        return sb;
    }

    @AfterThrowing(value = "logPointCut()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        log.info("【日志切面:AfterThrowing方法执行了】");
        // 保存异常日志记录
        log.error("发生异常时间：{}" +new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
        log.error("抛出异常：{}" + throwable.getMessage());
    }
}
